/* 打开LCD设备节点， 获取分辨率等参数， 映射Framebuff， 最后实现描点 */
/* 1.open函数：成功返回文件描述符，失败-1
 * #include <sys/types.h>
 * #include <sys/stat.h>
 * #include <fcntl.h>
 *
 * Linux for open: include <fcntl.h>
 * 	 for close: include <unistd.h>

 * int open(const char *pathname, int flags);路径，打开文件的方式
 * int open(const char *pathname, int flags, mode_t mode);创建文件的权限
 * ① pathname 表示打开文件的路径；
 * ② Flags表示打开文件的方式，常用的有以下6种，
 	a. O_RDWR表示可读可写方式打开;
 	b. O_RDONLY表示只读方式打开;
 	c. O_WRONLY表示只写方式打开;
 	d. O_APPEND 表示如果这个文件中本来是有内容的，则新写入的内容会接续到原来内容的后面;
 	e. O_TRUNC表示如果这个文件中本来是有内容的，则原来的内容会被丢弃，截断；
 	f. O_CREAT表示当前打开文件不存在，我们创建它并打开它，通常与O_EXCL结合使用，当没有文件时创建文件，有这个文件时会报错提醒我们；
 * ③ Mode表示创建文件的权限，只有在flags中使用了O_CREAT时才有效，否则忽略。

 */

/* 2.ioctl函数：成功返回文件描述符，失败-1
 * #include <sys/ioctl.h>

 * int ioctl(int fd, unsigned long request, ...);文件描述符，与驱动程序交互的命令， ...可变参数，驱动程序返回输出的数据
 */

/* 3.mmap函数：成功映射，将返回指向映射的区域的指针，失败将返回-1
 * #include <sys/mman.h>

 * void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
 * 
 * ① addr表示指定映射的內存起始地址，通常设为 NULL表示让系统自动选定地址，并在成功映射后返回该地址；
 * ② length表示将文件中多大的内容映射到内存中；
 * ③ prot 表示映射区域的保护方式，可以为以下4种方式的组合
 	a. PROT_EXEC 映射区域可被执行
 	b. PROT_READ 映射区域可被读出
 	c. PROT_WRITE 映射区域可被写入
 	d. PROT_NONE 映射区域不能存取
 * ④ Flags 表示影响映射区域的不同特性，常用的有以下两种
 	a. MAP_SHARED 表示对映射区域写入的数据会复制回文件内，原来的文件会改变。
 	b. MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制，对此区域的任何修改都不会写回原来的文件内容中。
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//open
#include <unistd.h>//close

#include <linux/fb.h>
#include <sys/ioctl.h>//ioctl

#include <sys/mman.h>//mmap munmap

#include <stdio.h>//printf
#include <string.h>

static int fd_fb;//文件描述符
static struct fb_var_screeninfo var;//LCD驱动可变参数
static int screen_size;//整个Framebuff的大小
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;

/* 4.描点函数 */
/**********************************************************************
 * 函数名称： lcd_put_pixel
 * 功能描述： 在LCD指定位置上输出指定颜色（描点）
 * 输入参数： x坐标，y坐标，颜色
 * 输出参数： 无
 * 返 回 值： 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/10/13	     V1.0	  xiongzh	      创建
 ***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)//传入颜色：格式永远是：0x00RRGGBB
{
	unsigned char *pen_8 = fb_base + y * line_width + x * pixel_width;//像素对应的Framebuff地址
	unsigned short *pen_16;
	unsigned int *pen_32;

	unsigned int red, green, blue;

	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int *)pen_8;

	switch (var.bits_per_pixel)
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16:
		{
			/* 565 */
			red = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue = (color >> 0) & 0xff;
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	}
}

int main(int argc, char * * argv)
{
	int i;
	
	/* 1.打开设备节点 */
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	
	/* 2.获取LCD参数 #include <linux/fb.h>*/
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\n");
		return -1;
	}
	
	/* 3.映射Framebuff */
	line_width = var.xres * var.bits_per_pixel / 8;
	pixel_width = var.bits_per_pixel / 8;
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
	fb_base = (unsigned char *)mmap(NULL, screen_size, PROT_READ |PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}
	
	/* 清屏：全部设为白色 */
	memset(fb_base, 0xff, screen_size);

	/* 随便设置出100个为红色 */
	for (i = 0; i < 100; i++)
	{
		lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
	}
	
	munmap(fb_base, screen_size);
	close(fd_fb);
	
	return 0;
}


